package org.chinacloud.wanghui.jdownload.downloader;

import org.chinacloud.wanghui.jdownload.Downloader;

import java.io.*;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;

/**
 * Created with IntelliJ IDEA.
 * User: wanghui
 * Date: 13-10-31
 * Time: 下午3:26
 * To change this template use File | Settings | File Templates.
 */
public class DownloadConnection implements Runnable{
    private ConnectionContext context = new ConnectionContext();
    private AbstractDownloader downloader;
    //下载链接的开始处
    long bytesBegin;
    //下载链接的结束处
    long bytesEnd;
    //已经完成的字节数
    long bytesRead;

    final static int BUFFER_SIZE = 64 * 1024;
    //最大内存文件缓冲区
    final static int MAX_MEMORY_BUFFER_SIZE = 10 * 1024 * 1024;
    //网络链接流
    InputStream is = null;
    //链接线程名
    String connectionName ="unknown";
    //现场是否在运行
    boolean isRunning;
    public DownloadConnection(){
        super();
    }
    public DownloadConnection(long bytesBegin,long bytesEnd,AbstractDownloader downloader){
        super();
        this.bytesBegin = bytesBegin;
        this.bytesEnd = bytesEnd;
        bytesRead = 0;
        this.downloader = downloader;
    }


    public void start(){
        isRunning = true;
        new Thread(this).start();
    }


    @Override
    public void run() {
        try{
            if((bytesRead + bytesBegin) >= bytesEnd){
                throw new IOException("Connection has finished");
            }
            downloader.onBegin(context);
            is = new BufferedInputStream(downloader.openConnection(context, bytesBegin + bytesRead, bytesEnd));
            long bytesToRead = bytesEnd - bytesBegin;
            long stageToRead = bytesToRead - bytesRead;
            if(stageToRead > MAX_MEMORY_BUFFER_SIZE){
                stageToRead = MAX_MEMORY_BUFFER_SIZE;
            }
            long stageRead = bytesRead;
            int len;
            byte[] buffer = new byte[BUFFER_SIZE];

            System.out.println(String.format("Connection:%s Start,download offset: %d,end: %d",connectionName,bytesBegin,bytesEnd));

            MappedByteBuffer mappedByteBuffer = downloader.fileChannel.map(
                    FileChannel.MapMode.READ_WRITE,bytesBegin + bytesRead,stageToRead
            );
            System.out.println(String.format("Connection: %s Map memory,offset:%d,size:%d",connectionName,bytesBegin + bytesRead,stageToRead));

            try{
                while(downloader.running){
                    len = is.read(buffer);
                    if(len == -1){
                        System.out.println("connection network disconnected");
                        break;
                    }
                    if((bytesRead + len) >= bytesToRead){
                        len = (int)(bytesToRead - bytesRead);
                        mappedByteBuffer.put(buffer,0,len);
                        bytesRead += len;
                        System.out.println(String.format("connection: %s Download finished",connectionName));
                        break;
                    }else if((bytesRead + len - stageRead) >= stageToRead){
                        int tmpLen = (int)(stageToRead - bytesRead + stageRead);
                        mappedByteBuffer.put(buffer,0,tmpLen);
                        bytesRead += tmpLen;
                        stageRead = bytesRead;
                        AbstractDownloader.unmapFileChannel(mappedByteBuffer);
                        stageToRead = bytesToRead - bytesRead;
                        if(stageToRead > MAX_MEMORY_BUFFER_SIZE){
                            stageToRead = MAX_MEMORY_BUFFER_SIZE;
                        }
                        mappedByteBuffer = downloader.fileChannel.map(FileChannel.MapMode.READ_WRITE,bytesBegin + bytesRead,stageToRead);
                        mappedByteBuffer.put(buffer,tmpLen,len - tmpLen);

                        System.out.println(String.format("Connection: %s Map memory,offset: %d,size: %d",connectionName,bytesBegin + bytesRead,stageToRead));
                        continue;
                    }
                    mappedByteBuffer.put(buffer,0,len);
                    bytesRead += len;

                }
            }catch(Exception e){
                System.out.println(String.format("Connection:%s Exception caught: %s",connectionName,e.getMessage()));
            }
            AbstractDownloader.unmapFileChannel(mappedByteBuffer);
            mappedByteBuffer = null;
            is.close();
            downloader.closeConnection(context);
        }catch(FileNotFoundException e){
            System.out.println(String.format("Connection:%s Exception caught: %s",connectionName,e.getMessage()));
        }catch(IOException e){
            System.out.println(String.format("Connection:%s Exception caught: %s",connectionName,e.getMessage()));
        }finally {
            if(is != null){
                synchronized (is){
                    is = null;
                }
            }
            isRunning = false;
            synchronized (downloader){
                if(isRunning && (bytesRead + bytesBegin) < bytesEnd){
                    start();
                    System.out.println(String.format("Connection:%s disconnected,trying to reconnect.",connectionName));
                }else{
                    downloader.aliveConnectionCount -= 1;
                    if(!downloader.isDownloading()){
                        System.out.println("All Connection's thread exited!");
                        try{
                            downloader.fileChannel.close();
                        }catch(IOException e){
                            System.out.println(String.format("Connection:%s Exception caught: %s",connectionName,e.getMessage()));
                        }
                        try{
                            downloader.randomAccessFile.close();
                            System.out.println(String.format("File close: %s",new File(downloader.savePathString).getAbsolutePath()));
                        }catch(IOException e){
                            System.out.println(String.format("Connection:%s Exception caught: %s",connectionName,e.getMessage()));
                        }
                    }
                }
            }
        }
    }

}
